#include <stdio.h>
#include <stdlib.h>
#include <error.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mount.h>
#include <unistd.h>
#include <pthread.h>

#define FILE_DEV "/proc/scsi/scsi" /* 检测设备数和类型 */
#define FILE_MOUNT "/proc/mounts"  /* 检测设备是否被mount */
#define FILE_DISC_PARTS "/proc/partitions" /* 检测设备的分区情况 */
#define FILE_DEV_STATUS "/proc/scsi/usb-storage/%d" /* 检测某一设备是否在插入状态 */
#define FILE_DEV_PART "/dev/scsi/host%d/bus0/target0/lun0/" /* 具体的设备 */
#define USB_CDROM_MP "/tmp/cdrom"
#define USB_DISK_MP "/tmp/usbdisk"
#define MAX_NAME_LEN 64
#define MAX_PART_NUM 6

struct scsi_usb_dev
{
    int type; /* 1 cdrom 2 disk */
    int index; /* like host0 host1 */
    char file_status[MAX_NAME_LEN];
    char devfile_parts[MAX_PART_NUM][MAX_NAME_LEN];
    char mount_path[MAX_PART_NUM][MAX_NAME_LEN];
    int part_num;
    struct scsi_usb_dev * next_dev; /* point next device */
};

static int is_manual_umount = 1;
static struct scsi_usb_dev * f_first_dev = NULL;
static int check_parts(struct scsi_usb_dev * dev);


static int add_dev(struct scsi_usb_dev * dev)
{
    if (f_first_dev) {
        dev->next_dev = f_first_dev;
        f_first_dev = dev;
    }
    else {
        f_first_dev = dev;
        dev->next_dev = NULL;
    }

    return 0;
}

static int init_dev(struct scsi_usb_dev * dev, int index, char * type)
{
    dev->index = index;
    sprintf(dev->file_status, FILE_DEV_STATUS, index, index);
    if (!strncmp(type, "CD-ROM", 6)) {
        dev->type = 1;
        dev->part_num = 1;
        sprintf(dev->devfile_parts[0], FILE_DEV_PART"cd", index);
        strcpy(dev->mount_path[0], USB_CDROM_MP);
        printf("###%d###, %s, %s\n", dev->index, dev->devfile_parts[0], dev->mount_path[0]);
    }
    else {
        dev->type = 2; /* usb disk */
        dev->part_num = check_parts(dev);
    }

    return 0;
}

static int check_parts(struct scsi_usb_dev * dev)
{
    char buf[1024];
    char hoststr[16];
    int fd, len;
    int part_num = 0;
    char * line;
    char * delim = "\n";
    char * strtok_tmp_ptr;
    char * seek;
    char * part_blocks;

    fd = open(FILE_DISC_PARTS, O_RDONLY);
    if (fd > 0) {
        len = read(fd, buf, sizeof(buf));
        close(fd);

        if (len > 0) {
            sprintf(hoststr, "scsi/host%d", dev->index);
            line = strtok_r(buf, delim, &strtok_tmp_ptr);
            while (line) {
                seek = strstr(line, hoststr);
                if (seek) {
                    part_blocks = seek -3;
                    if (strncmp(part_blocks, "1", 3)) {
                        /* not extend part */
                        sprintf(dev->devfile_parts[part_num], "/dev/%s", seek);
                        printf("/dev/%s\n", seek);
                        sprintf(dev->mount_path[part_num], USB_DISK_MP"/disk%d/part%d", dev->index, part_num);
                        printf("===%d===, %s, %s\n", dev->index, dev->devfile_parts[part_num], dev->mount_path[part_num]);
                        part_num++;
                        if (part_num == MAX_PART_NUM)
                            break;
                    }
                }
                line = strtok_r(NULL, delim, &strtok_tmp_ptr);
            }
        }
    }
    else
        perror(FILE_DISC_PARTS);

    return part_num;
}

static void clear_dev(void)
{
    struct scsi_usb_dev * cur_dev = f_first_dev;
    struct scsi_usb_dev * tmp_dev;

    while (cur_dev) {
        tmp_dev = cur_dev;
        cur_dev = cur_dev->next_dev;
        free(tmp_dev);
    }

    f_first_dev = NULL;
}

static int find_device()
{
    char buf[1024];
    int fd;
    int len;
    int dev_num = 0;
    char * seek = NULL;
    struct scsi_usb_dev * new_dev;

    clear_dev();
    fd = open(FILE_DEV, O_RDONLY);
    printf("fd = %d\n", fd);
    if (fd > 0) {
        len = read(fd, buf, sizeof(buf));
        close(fd);
        if (len > 0) {
            seek = buf;
            while (seek) {
                seek = strstr(seek, "Host: scsi");
                if (seek) {
                    seek += strlen("Host: scsi");
                    seek = strstr(seek, "Type");
                    if (seek) {
                        seek += strlen("Type");
                        while (* seek == ' ')
                            seek++;
                        new_dev = malloc(sizeof(struct scsi_usb_dev));
                        init_dev(new_dev, dev_num, seek);
                        add_dev(new_dev);
                        dev_num++;
                    }
                }
            }
            printf("dev_num = %d\n", dev_num);
        }
    }
    else {
        printf("file opne failed.\n");
        perror(FILE_DEV);
    }
    printf("dev_num = %d\n", dev_num);

    return dev_num;
}

static int check_attach(struct scsi_usb_dev * dev)
{
    char buf[512];
    int fd;
    int len;
    char * seek;

    fd = open(dev->file_status, O_RDONLY);
    if (fd > 0) {
        len = read(fd, buf, sizeof(buf));
        close(fd);
        if (len > 0) {
            seek = strstr(buf, "Attached:");
            if (seek) {
                seek += strlen("Attached:");
                while (* seek ==  ' ')
                    seek++;
                return * seek == 'Y';
            }
        }
    }
    else
        perror(dev->file_status);

    return 0;
}

static int check_mount(struct scsi_usb_dev * dev)
{
    char buf[1024];
    int fd;
    int len;
    char * seek;
    int i = 0;

    fd = open(FILE_MOUNT, O_RDONLY);
    if (fd > 0) {
        len = read(fd, buf, sizeof(buf));
        close(fd);
        if (len > 0) {
            buf[len] = '\0';
            if (dev->type == 2 && dev->part_num > 1)
                i++;
            for (; i < dev->part_num; i++) {
                seek = strstr(buf, dev->devfile_parts[i]);
                if (seek != NULL) 
                    return -1;
            }
        }
    }
    perror(FILE_MOUNT);

    return 0;
}

static int do_mount(struct scsi_usb_dev * dev)
{
    int i = 0;
    char fstype[10];
    unsigned long mountflags = 0;
    char mount_data[30];
    char tmpdir[50];
    int mount_ok = 0;
    int is_vcd = 0;
    char check_cmd[50];

    if (dev->type == 1 && is_manual_umount == 0) {
        strcpy(fstype, "iso9660");
        mountflags = 0xc0ed0000 | MS_RDONLY;
        strcpy(mount_data, "codepage=936, iocharset=bg2312");
        if (mount(dev->devfile_parts[0], dev->mount_path[0], fstype, mountflags, mount_data) == 0) {
            mount_ok = 1;
            printf("mount -t %s %s %s success.\n", fstype, dev->devfile_parts[0], dev->mount_path[0]);

            /* check is vcd */
            sprintf(check_cmd, "ls %s/vcd/*.vcd", dev->mount_path[0]);
            is_vcd = (system(check_cmd) == 0);
            if (is_vcd) {
                if (umount(dev->mount_path[0]) == 0)
                    printf("umount %s success (vcd iso9660)\n", dev->devfile_parts[0]);
                else {
                    is_vcd = 0;
                    printf("umount %s failed (vcd iso9660)\n", dev->devfile_parts[0]);
                }
            }
        }
        else {
            mount_ok = 0;
        }

        if (mount_ok == 0 || is_vcd) {
            printf("mount -t %s %s %s failed, try cdfs\n", fstype, dev->devfile_parts[0], dev->mount_path[0]);
            strcpy(fstype, "cdfs");
            if (mount(dev->devfile_parts[0], dev->mount_path[0], fstype, mountflags, mount_data) == 0) {
                printf("mount -t %s %s %s success.\n", fstype, dev->devfile_parts[0], dev->mount_path[0]);
                return 0;
            }
            else {
                printf("mount -t %s %s %s failed, try cdfs.\n", fstype, dev->devfile_parts[0], dev->mount_path[0]);
                return -1;
            }
        }
    }
    else if (dev->type == 2) {
        sprintf(tmpdir, USB_DISK_MP"/disk%d", dev->index);
        mkdir(tmpdir, 0777);
        strcpy(fstype, "vfat");
        mountflags = 0xc0ed0000;
        strcpy(mount_data, "codepage=936, iocharset=gb2312");
        if (dev->part_num > 1)
            i++; /* if disk ignore first part disc */

        for (; i < dev->part_num; i++) /* if disk ignore first part disc */ {
            mkdir(dev->mount_path[i], 0777);
            if(mount(dev->devfile_parts[i], dev->mount_path[i], fstype, mountflags, mount_data) == 0) {
                printf("mount %s %s success.\n", dev->devfile_parts[i], dev->mount_path[i]);
                // do_usb_disk_update(dev->mount_path[i]);
            }
            else {
                rmdir(dev->mount_path[i]);
                printf("mount %s %s failed.\n", dev->devfile_parts[i], dev->mount_path[i]);
            }
        }
    }
    else {
        printf("not cd or usb disk.\n");
        return -1;
    }

    return 0;
}

static int do_umount(struct scsi_usb_dev * dev)
{
    int i = 0; 
    char tmpdir[50];

    if (dev->type == 1) {
        if (umount(dev->mount_path[0] == 0)) {
            printf("umount %s success.\n", dev->devfile_parts[0]);
            return 0;
        }
        else {
            printf("umount %s failed.\n", dev->devfile_parts[0]);
            return -1;
        }
    }
    else if (dev->type == 2) {
        if (dev->part_num > 1)
            i++;
        for (; i < dev->part_num; i++) {
            if (umount(dev->mount_path[i]) == 0) {
                printf("umount %s success.\n", dev->devfile_parts[i]);
                remove(dev->mount_path[i]);
            }
            else {
                printf("umount %s failed.\n", dev->devfile_parts[i]);
            }
        }

        sprintf(tmpdir, USB_DISK_MP"/disk%d", dev->index);
        remove(tmpdir);
    }

    return 0;
}

static int process_dev(struct scsi_usb_dev * dev)
{
    if (check_attach(dev)) { /* 检测设备是否插上 */
        if (!check_mount(dev))
            do_mount(dev);
    }
    else {
        if (check_mount(dev))
            do_umount(dev);
    }

    return 0;
}

void manual_umount(void)
{
    struct scsi_usb_dev * cur_dev = NULL;

    cur_dev = f_first_dev;

    while (cur_dev) {
        if (cur_dev->type == 1)
            break;
        cur_dev = cur_dev->next_dev;
    }

    if (cur_dev != NULL) {
        is_manual_umount = 1;
        if (check_mount(cur_dev)) 
            if (do_umount(cur_dev))
                printf("光驱卸载成功\n");
    }
}

void manual_mount()//争对光驱进行mount
{
    /* now only umount cdrom*/
    struct scsi_usb_dev * cur_dev = NULL;
    cur_dev = f_first_dev;

    while (cur_dev) {
        if (cur_dev->type == 1 ) 
            break;
        cur_dev = cur_dev->next_dev;
    }

    if (cur_dev != NULL)
    {
        is_manual_umount = 0;
        if (!check_mount(cur_dev))
        {
            do_mount(cur_dev);
        }
    }
}

int main()
{
    struct scsi_usb_dev * cur_dev = NULL;

    mkdir (USB_DISK_MP, 0777);
    mkdir (USB_CDROM_MP, 0777);

    while (1) {
        find_device();
#if 1
        cur_dev = f_first_dev;
        while (cur_dev) {
            process_dev(cur_dev);
            cur_dev = cur_dev->next_dev;
        }
#endif
        sleep(10);
    }
}
